今天要加入遊戲中一個重要的元素,玩家控制的平板,進行左右的控制,球若碰觸到平板時,會反彈.
好的那我們先把玩家控制的平板畫出來,在這裡先但停一下,我們往後面開發思考下,之後要化的組件還有磚塊,這些都是矩形的元件,為了減少重複程式碼的撰寫,我們可以把會重複使用的組建寫成元件,我們就以這控制版作為起始吧.
drawRectangle = (topLeftX, topLeftY, boxWidth, boxHeight, color) => {
canvasContext.fillStyle = color;
canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
};
上面我們建立了一個叫drawRectangle
的元件,他的輸入依序是,矩形右上角的X座標、Y做標、矩形寬、矩形高最後是填充顏色,之後若需要畫矩形,就只需要呼叫這函式即可,我們馬上就試試看吧.
我們現在要畫的矩形有兩個,一個是我們的背景,另一個是今天要加入的paddle,首先呢先加入paddle的參數.
// paddle參數
const PADDLE_WIDTH = 100;
const PADDLE_HEIGHT = 10;
const PADDLE_HIGH = 50; // paddle 距離底部高度
var paddle_x = 400;
paddle的厚度、寬度以及距離底部的高度,整個遊戲過程中不會改變,那我們在宣告時用const
.接下來我們來修改draw
的函式.
// 負責畫畫
draw = () => {
// background
drawRectangle(0, 0, canvas.width, canvas.height, 'black');
// paddle
drawRectangle(paddle_x, canvas.height - PADDLE_HEIGHT - PADDLE_HIGH,
PADDLE_WIDTH, PADDLE_HEIGHT, 'white');
// 畫球
canvasContext.fillStyle = 'white';
canvasContext.beginPath();
canvasContext.arc(ball_x, ball_y, 10, 0, Math.PI * 2);
canvasContext.fill();
}
執行後會看到下面畫面,我們用元件的方式正常顯示了背景,同時也畫了白色的paddle.
這時候是不是覺得畫一顆球要四行程式碼很冗呢?那就一起元件化吧!
drawCircle = (centerX, centerY, r, color) => {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, r, 0, Math.PI * 2);
canvasContext.fill();
}
// 負責畫畫
draw = () => {
// background
drawRectangle(0, 0, canvas.width, canvas.height, 'black');
// paddle
drawRectangle(paddle_x, canvas.height - PADDLE_HEIGHT - PADDLE_HIGH,
PADDLE_WIDTH, PADDLE_HEIGHT, 'white');
// 畫球
drawCircle(ball_x, ball_y, ball_r, 'wight');
}
看起來精簡了不少,真是舒服~
接下來呢,要控制paddle的移動啦,不能讓它一直固定在相同位置上,這樣遊戲怎麼玩呢!?
Canvas可以讀取游標的位置,但如果要讀取鍵盤按鍵的輸入,需要用其他方法來做控制,Canvas並沒有直接支援.那在這邊就先以較直接的方式,用抓游標位置的方式來控制.
先寫個抓取游標位置的函式:
// 游標位置
mousePos = (event) => {
var rect = canvas.getBoundingClientRect();
var root = document.documentElement;
var mouse_x = event.clientX - rect.left - root.scrollLeft;
// 設定paddle控制點在中央
paddle_x = mouse_x - PADDLE_WIDTH / 2;
}
然後將函式加入window.onload
中:
window.onload = () => {
canvas = document.getElementById('playground');
canvasContext = canvas.getContext('2d');
//一秒更新幾次畫面
var timesPerSec = 30;
setInterval(drawAll, 1000 / timesPerSec);
canvas.addEventListener('mousemove', mousePos);
}
執行看看,現在paddle應該會跟著游標跑囉~
到了這邊,今天還剩下兩個任務,paddle要能反彈球還有就是求如果掉到畫面下要重置.
先來處理比較麻煩的反彈吧,首先要先知道paddle的四的角的座標,這部分呢因為跟球的移動有關,所以我們寫在move
函式中.
// 定義paddle四個角的座標
var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
var paddleLeftEdgeX = paddle_x;
var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;
接下來就是求接觸到paddle後,反彈
// 碰到paddle反彈
if (ball_y > (paddleTopEdgeY - ball_r) &&
ball_y < paddleBottomEdgeY &&
ball_x > paddleLeftEdgeX &&
ball_x < paddleRightEdgeX) {
ball_speed_y *= -1;
}
趕緊來執行看看.
終於剩下重置啦~~~
首先呢我們先來定義一下重置,我們希望球重置後發球時,會在同一個位置開球,並且移動方向相同.OK 寫成一個函式吧
// 重置球
resetBall = () => {
ball_x = 300;
ball_y = 300;
ball_speed_x = 5;
ball_speed_y = 5;
}
然後修改一下move裡面觸及底部邊界的動作
move = () => {
ball_x += ball_speed_x;
ball_y += ball_speed_y;
// 碰觸邊界動作
if (ball_x < (0 + ball_r)) {
ball_speed_x *= -1;
}
if (ball_x > (canvas.width - ball_r)) {
ball_speed_x *= -1;
}
if (ball_y < (0 + ball_r)) {
ball_speed_y *= -1;
}
if (ball_y > canvas.height) {
resetBall();
}
// 定義paddle四個角的座標
var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
var paddleLeftEdgeX = paddle_x;
var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;
// 碰到paddle反彈
if (ball_y > (paddleTopEdgeY - ball_r) &&
ball_y < paddleBottomEdgeY &&
ball_x > paddleLeftEdgeX &&
ball_x < paddleRightEdgeX) {
ball_speed_y *= -1;
}
}
執行一下,看看今天的成果吧.
反彈功能,還有重置球都正常!!恭喜恭喜
最後依然附上到目前進度的完整程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<title>First Game</title>
<meta name="description" content="第一個遊戲">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<canvas id="playground" width="800" height="630"></canvas>
<script>
var canvas, canvasContext;
// 球的參數
var ball_x = 300;
var ball_y = 300;
var ball_r = 10;
var ball_speed_x = 5;
var ball_speed_y = 5;
// paddle參數
const PADDLE_WIDTH = 100;
const PADDLE_HEIGHT = 10;
const PADDLE_HIGH = 50; // paddle 距離底部高度
var paddle_x = 400;
window.onload = () => {
canvas = document.getElementById('playground');
canvasContext = canvas.getContext('2d');
//一秒更新幾次畫面
var timesPerSec = 30;
setInterval(drawAll, 1000 / timesPerSec);
canvas.addEventListener('mousemove', mousePos);
}
// 負責更新畫面
drawAll = () => {
move();
draw();
}
// 負責畫畫
draw = () => {
// background
drawRectangle(0, 0, canvas.width, canvas.height, 'black');
// paddle
drawRectangle(paddle_x, canvas.height - PADDLE_HEIGHT - PADDLE_HIGH,
PADDLE_WIDTH, PADDLE_HEIGHT, 'white');
// 畫球
drawCircle(ball_x, ball_y, ball_r, 'wight');
}
// 負責處理動作
move = () => {
ball_x += ball_speed_x;
ball_y += ball_speed_y;
// 碰觸邊界動作
if (ball_x < (0 + ball_r)) {
ball_speed_x *= -1;
}
if (ball_x > (canvas.width - ball_r)) {
ball_speed_x *= -1;
}
if (ball_y < (0 + ball_r)) {
ball_speed_y *= -1;
}
if (ball_y > canvas.height) {
resetBall();
}
// 定義paddle四個角的座標
var paddleTopEdgeY = canvas.height - PADDLE_HEIGHT - PADDLE_HIGH;
var paddleBottomEdgeY = paddleTopEdgeY + PADDLE_HEIGHT;
var paddleLeftEdgeX = paddle_x;
var paddleRightEdgeX = paddleLeftEdgeX + PADDLE_WIDTH;
// 碰到paddle反彈
if (ball_y > (paddleTopEdgeY - ball_r) &&
ball_y < paddleBottomEdgeY &&
ball_x > paddleLeftEdgeX &&
ball_x < paddleRightEdgeX) {
ball_speed_y *= -1;
}
}
// 矩形元件
drawRectangle = (topLeftX, topLeftY, boxWidth, boxHeight, color) => {
canvasContext.fillStyle = color;
canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
}
// 圓形元件
drawCircle = (centerX, centerY, r, color) => {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, r, 0, Math.PI * 2);
canvasContext.fill();
}
// 游標位置
mousePos = (event) => {
var rect = canvas.getBoundingClientRect();
var root = document.documentElement;
var mouse_x = event.clientX - rect.left - root.scrollLeft;
// 設定paddle控制點在中央
paddle_x = mouse_x - PADDLE_WIDTH / 2;
}
// 重置球
resetBall = () => {
ball_x = 300;
ball_y = 300;
ball_speed_x = 5;
ball_speed_y = 5;
}
</script>
</body>
</html>
那就明天再加油啦~